package com.gitee.easyopen.session;

import java.io.Serializable;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.TimeUnit;

import javax.servlet.ServletContext;
import javax.servlet.http.HttpSession;
import javax.servlet.http.HttpSessionContext;

import org.springframework.data.redis.core.HashOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.util.Assert;

/**
 * RedisHttpSession
 * @author tanghc
 *
 */
@SuppressWarnings("deprecation")
public class RedisHttpSession implements HttpSession ,Serializable {
    private static final long serialVersionUID = -8081963657251144855L;

    public static final String SESSION_PREFIX = "session:";
    private static final String SESSION_ATTR = "session_attr:";
    private static final String CREATION_TIME = "creationTime";
    private static final String LAST_ACCESSED_TIME = "lastAccessedTime";
    private static final String MAX_INACTIVE_INTERVAL = "maxInactiveInterval";

    private String key;
    private String id;
    private long creationTime;
    private long lastAccessedTime;
    private int maxInactiveInterval;

    private ServletContext servletContext;

    private RedisTemplate redisTemplate;

    private RedisHttpSession(){}


    /**
     * 创建新的session
     * @param servletContext
     * @param sessionId
     * @param sessionTimeout 失效时间，秒
     * @param redisTemplate
     */
    private RedisHttpSession(ServletContext servletContext,String sessionId,int sessionTimeout, RedisTemplate redisTemplate) {
        Assert.notNull(redisTemplate, "redisTemplate不能为null");
        this.servletContext = servletContext;
        this.redisTemplate = redisTemplate;
        this.id = this.buildId(sessionId);
        this.key = buildKey(id);
        this.creationTime = System.currentTimeMillis();
        this.lastAccessedTime = creationTime;
        this.maxInactiveInterval = sessionTimeout * 60;// 分转换成秒
        //save to redis
        saveSession();
    }
    
    protected String buildId(String id) {
        return (id != null ? id : UUID.randomUUID().toString().replace("-", "").toUpperCase());
    }
    
    public static String buildKey(String sessionId) {
        return SESSION_PREFIX + sessionId;
    }

    /**
     * 创建新的session
     * @param servletContext
     * @param sessionId
     * @param sessionTimeout 失效时间，秒
     * @param redisTemplate
     * @return
     */
    public static RedisHttpSession createNewSession(ServletContext servletContext, String sessionId, int sessionTimeout, RedisTemplate redisTemplate){
        return new RedisHttpSession(servletContext, sessionId, sessionTimeout, redisTemplate);
    }

    /**
     * 创建已经存在的session,数据在redis里面
     * @param sessionId
     * @param servletContext
     * @param redisTemplate
     * @return
     */
    public static RedisHttpSession createExistSession(String sessionId, ServletContext servletContext, RedisTemplate redisTemplate){
        RedisHttpSession redisHttpSession= new RedisHttpSession();
        redisHttpSession.setId(sessionId);
        redisHttpSession.setKey(buildKey(sessionId));
        redisHttpSession.setRedisTemplate(redisTemplate);
        redisHttpSession.setServletContext(servletContext);
        redisHttpSession.refresh();

        return redisHttpSession;
    }

    public void setId(String id) {
        this.id = id;
    }


    public void setServletContext(ServletContext servletContext) {
        this.servletContext = servletContext;
    }

    @Override
    public long getCreationTime() {
        return (Long) this.redisTemplate.opsForHash().get(key, CREATION_TIME);
    }

    @Override
    public String getId() {
        return id;
    }

    @Override
    public long getLastAccessedTime() {
        return (Long) this.redisTemplate.opsForHash().get(key, LAST_ACCESSED_TIME);
    }

    @Override
    public ServletContext getServletContext() {
        return servletContext;
    }

    @Override
    public void setMaxInactiveInterval(int interval) {
        this.redisTemplate.opsForHash().put(key, MAX_INACTIVE_INTERVAL, interval);
    }

    @Override
    public int getMaxInactiveInterval() {
        return (Integer)this.redisTemplate.opsForHash().get(key, MAX_INACTIVE_INTERVAL);
    }

    @Override
    public HttpSessionContext getSessionContext() {
        return null;
    }

    @Override
    public Object getAttribute(String name) {
        return this.redisTemplate.opsForHash().get(key, SESSION_ATTR + name);
    }

    @Override
    public Object getValue(String name) {
        return getAttribute(name);
    }

    @Override
    public Enumeration<String> getAttributeNames() {
        return Collections.enumeration(getAttributeKeys());
    }

    private Set<String> getAttributeKeys(){
        Set keys = this.redisTemplate.opsForHash().keys(key);
        Set<String> attrNames = new HashSet<>();
        for (Object key : keys){
            String k = String.valueOf(key);
            if (k.startsWith(SESSION_ATTR)){
                attrNames.add(k.substring(SESSION_ATTR.length()));
            }
        }
        return attrNames;
    }


    @Override
    public String[] getValueNames() {
        return getAttributeKeys().toArray(new String[0]);
    }

    @Override
    public void setAttribute(String name, Object value) {
        this.redisTemplate.opsForHash().put(key, SESSION_ATTR + name, value);
    }

    @Override
    public void putValue(String name, Object value) {
        setAttribute(name, value);
    }

    @Override
    public void removeAttribute(String name) {
        this.redisTemplate.opsForHash().delete(key, name);
    }

    @Override
    public void removeValue(String name) {
        removeAttribute(name);
    }

    @Override
    public void invalidate() {
        this.redisTemplate.delete(key);
    }

    @Override
    public boolean isNew() {
        return false;
    }

    private void saveSession(){
        HashOperations ops = this.redisTemplate.opsForHash();
        ops.put(key, LAST_ACCESSED_TIME, lastAccessedTime);
        ops.put(key, CREATION_TIME, creationTime);
        ops.put(key, MAX_INACTIVE_INTERVAL, maxInactiveInterval);
        refresh();
    }

    /**
     * update expire time
     */
    public void refresh(){
        this.redisTemplate.expire(key, getMaxInactiveInterval(), TimeUnit.SECONDS);
    }


    public void setLastAccessedTime(long lastAccessedTime) {
        this.redisTemplate.opsForHash().put(key, LAST_ACCESSED_TIME, lastAccessedTime);
    }

    public String getKey() {
        return key;
    }

    public void setKey(String key) {
        this.key = key;
    }

    public boolean isInvalidated() {
        return !this.redisTemplate.hasKey(key);
    }


    public void setRedisTemplate(RedisTemplate redisTemplate) {
        this.redisTemplate = redisTemplate;
    }
    

}